/** Import everything needed **/
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

  /**KYUHONG PARK, CS 211, Fall 2011
 Programming Assignment #1
 This program is a hangman game program that designed
  the requirement such as user-friendly GUI using Java swing frames.
 On this game, User will have 8 chances to guess and to win the game. 
  */

/**
 * This is a class for hangman.
 * @author Acie Park
 * @version 1.0
 */
public class HangmanGUI extends JFrame implements ActionListener {

	/**
	 * serialVersionUID for HangmanGUI class
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * There are 4 panels for drawing, showing words, showing letter buttons,
	 * control buttons.
	 */
	private final JPanel panelDRAW, panelWORD, panelLETTER, panelCONTROL;

	/**
	 * There are 3 sections of GRidBagLayout to organize hangman program.
	 */
	private final GridBagLayout layoutDRAW, layoutTOP, layoutBottom;

	/**
	 * There are 3 sections of GRidBagLayout Contraints for control.
	 */
	private final GridBagConstraints conDRAW, conTOP, conBOTTOM;

	/**
	 * There are 4 JTextField for showing word, showing tried number, showing
	 * appropriate message, showing chances that user have, showing oneshot.
	 */
	private final JTextField t_word, t_tried, t_msg, t_lchance, t_oneshot;

	/**
	 * Buttons for letter buttons, the start button, the reset button, the oneshot button.
	 */
	private final JButton[] b_letter;
	
	/**
	 * Buttons for the start, the reset, and the oneshot
	 */
	private final JButton b_start, b_reset, b_oneshot;
	
	/**
	 * Container variable
	 */
	private final Container con;
	
	/**
	 * current state of the running and the flag
	 */
	private boolean running, flag;
	
	/**
	 * lists for letters, storing answers, string selected letters and variables.
	 */
	private final char[] c_letter;
	
	/**
	 * temporary characters
	 */
	private final char[] temp1, temp2;
	
	/**
	 * temporary indexes
	 */
	private int temp1_index, temp2_index;
	
	/**
	 * fail number
	 */
	private int failnum;
	
	/**
	 * level value
	 */
	private int level;

	/**
	 * Font variable
	 */
	private final Font hangmanFont;

	/**
	 * for numeric literal.
	 */
	private static final int TWENTYFIVE = 25;
	
	/**
	 * for numeric literal.
	 */
	private static final int HUNDREAD = 100;
	
	/**
	 * for numeric literal.
	 */
	private static final int TEN = 10;
	
	/**
	 * for numeric literal.
	 */
	private static final int TWO = 2;
	
	/**
	 * for numeric literal.
	 */
	private static final int THREE = 3;
	
	/**
	 * for numeric literal.
	 */
	private static final int FOUR = 4;
	
	/**
	 * for numeric literal.
	 */
	private static final int FIVE = 5;
	
	/**
	 * for numeric literal.
	 */
	private static final int SIX = 6;

	/**
	 * for numeric literal.
	 */
	private static final int SEVEN = 7;
	
	/**
	 * for numeric literal.
	 */
	private static final int EIGHT = 8;

	/**
	 * for numeric literal.
	 */
	private static final int TWENTY = 20;
	
	/**
	 * for numeric literal.
	 */
	private static final int TWENTYSIX = 26;
	
	/**
	 * for numeric literal.
	 */
	private static final int THIRTY = 30;
	
	/**
	 * for numeric literal.
	 */
	private static final int THIRTYFIVE = 35;

	/**
	 * for numeric literal.
	 */
	private static final int FORTY = 40;
	
	/**
	 * for numeric literal.
	 */
	private static final int FOURFIVE = 45;

	/**
	 * for numeric literal.
	 */
	private static final int FIFTY = 50;
	
	/**
	 * for numeric literal.
	 */
	private static final int FIFTYTHREE = 53;
	
	/**
	 * for numeric literal.
	 */
	private static final int FIVEEIGHT = 58;

	/**
	 * for numeric literal.
	 */
	private static final int SIXFIVE = 65;
	
	/**
	 * for numeric literal.
	 */
	private static final int SEVENEIGHT = 78;
	
	/**
	 * for numeric literal.
	 */
	private static final int EIGHTYII = 80;

	/**
	 * for numeric literal.
	 */
	private static final int EIGHTYFIVE = 85;
	
	/**
	 * for numeric literal.
	 */
	private static final int NINETY = 90;

	/**
	 * for numeric literal.
	 */
	private static final int NINETYFIVE = 95;

	/**
	 * for numeric literal.
	 */
	private static final int ONETEN = 110;
	
	/**
	 * for numeric literal.
	 */
	private static final int ONESEVENFIVE = 175;

	/**
	 * for numeric literal.
	 */
	private static final int ONETHRTY = 130;

	/**
	 * for numeric literal.
	 */
	private static final int ONEFIFTY = 150;
	
	/**
	 * for numeric literal.
	 */
	private static final int ONESIXFIVE = 165;
	
	/**
	 * for numeric literal.
	 */
	private static final int TWOHUNDREAD = 250;
	
	/**
	 * for numeric literal.
	 */
	private static final int WIDTH_ONE = 650;
	
	/**
	 * for numeric literal.
	 */
	private static final int WIDTH_TWO= 950;
	
	/**
	 * for numeric literal.
	 */
	private static final int HEIGHT_ONE = 380;
	
	/**
	 * for numeric literal.
	 */
	private static final int HEIGHT_TWO = 500;

	/**
	 * previous JButton
	 */
	private JButton preButton = new JButton();
	
	/**
	 * letter JButton
	 */
	private JButton letterButton = new JButton();
	
	/**
	 * Hangman class
	 */
	private final Hangman hm = new Hangman();
	
	/**
	 * HMpanel class
	 */
	private final HMpanel hmp = new HMpanel();

	/**
	 * This is a constructor to hangman.
	 */
	public HangmanGUI() {
		super("Hangman");
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent arg0) {
				hmp.setVisible(true);
			}
		});
		hangmanFont = new Font("Courier", Font.BOLD, TWENTYFIVE);
		
		// To declare each panel.
		panelDRAW = new JPanel();
		panelWORD = new JPanel();
		panelLETTER = new JPanel();
		panelCONTROL = new JPanel();
		
		// To declare each Layout.
		layoutDRAW = new GridBagLayout();
		layoutTOP = new GridBagLayout();
		layoutBottom = new GridBagLayout();
		
		// To set conDRAW - the part of drawing.
		conDRAW = new GridBagConstraints();
		conDRAW.fill = GridBagConstraints.BOTH;
		conDRAW.weightx = 1.0;
		conDRAW.weighty = 1.0;
		conDRAW.ipady = HUNDREAD;
		
		// To set conTOP 
		conTOP = new GridBagConstraints();
		conTOP.fill = GridBagConstraints.BOTH;
		conTOP.weightx = 1.0;
		conTOP.weighty = 1.0;
		conTOP.ipady = TEN;
		
		// To set conBOTTOM
		conBOTTOM = new GridBagConstraints();
		conBOTTOM.fill = GridBagConstraints.BOTH;
		conBOTTOM.weightx = 1.0;
		conBOTTOM.weighty = 1.0;
		conBOTTOM.ipady = TEN;

		// To set the JTextField of t_word. 
		t_word = new JTextField("-----Hangman-----");
		t_word.setEditable(false);
		t_word.setFont(hangmanFont);
		
		// To set the JTextField of t_tried.
		t_tried = new JTextField();
		t_tried.setEditable(false);
		t_tried.setFont(hangmanFont);
		
		// To set the JTextField of t_msg.
		t_msg = new JTextField();
		t_msg.setEditable(false);
		
		t_oneshot = new JTextField();
		t_oneshot.setBackground(Color.WHITE);
		t_oneshot.setEditable(false);
		
		// To set the JTextField of t_lchance.
		t_lchance = new JTextField();
		t_lchance.setBackground(Color.pink);
		t_lchance.setEditable(false);
		
		con = this.getContentPane();
		con.setLayout(new BorderLayout());
		
		// To set the JButton of of each button. 
		b_start = new JButton("START");
		b_start.setBackground(Color.white);
		b_reset = new JButton("RESET");
		b_reset.setBackground(Color.white);
		b_oneshot = new JButton("ONESHOT");
		b_oneshot.setBackground(Color.white);
		
		// To declare and initialize each variables.
		running = false;
		flag = false;
		level = EIGHT;
		failnum = 0;
		b_letter = new JButton[TWENTYSIX];
		c_letter = new char[TWENTYSIX];
		temp1 = new char[THIRTY];
		temp2 = new char[THIRTY];
		char sel = 'A';
		// To insert letter from A to Z to the array of c_letter.
		for (int i = 0; i < TWENTYSIX; i++) {
			c_letter[i] = sel;
			sel++;
		}
		// To insert button from A to Z.
		for (int i = 0; i < TWENTYSIX; i++) {
			b_letter[i] = new JButton(String.valueOf(c_letter[i]));
			b_letter[i].setBackground(Color.white);
		}
		
		panelDRAW.setLayout(layoutDRAW);
		conDRAW.gridx = 0;
		conDRAW.gridy = 0;
		conDRAW.gridwidth = 1;
		conDRAW.gridheight = 1;
		layoutDRAW.setConstraints(hmp, conDRAW);
		panelDRAW.add(hmp);
		
		panelWORD.setLayout(layoutTOP); 
		conTOP.gridx = 0;
		conTOP.gridy = TWO;
		conTOP.gridwidth = EIGHT;
		conTOP.gridheight = 1;
		layoutTOP.setConstraints(t_word, conTOP);
		panelWORD.add(t_word);
		
		conTOP.gridx = 0;
		conTOP.gridy = THREE;
		conTOP.gridwidth = EIGHT;
		conTOP.gridheight = 1;
		layoutTOP.setConstraints(t_tried, conTOP);
		panelWORD.add(t_tried);
		
		conTOP.gridx = 0;
		conTOP.gridy = FOUR;
		conTOP.gridwidth = 1;
		conTOP.gridheight = 1;
		layoutTOP.setConstraints(new Label("message : ", Label.CENTER), conTOP);
		panelWORD.add(new Label("message : ", Label.CENTER));

		conTOP.gridx = 1;
		conTOP.gridy = FOUR;
		conTOP.gridwidth = SEVEN;
		conTOP.gridheight = 1;
		layoutTOP.setConstraints(t_msg, conTOP);
		panelWORD.add(t_msg);
		
		conTOP.gridx = TWO;
		conTOP.gridy = FIVE;
		conTOP.gridwidth = 1;
		conTOP.gridheight = 1;
		layoutTOP.setConstraints(new Label("CHANCE : ", Label.CENTER), conTOP);
		panelWORD.add(new Label("CHANCE : ", Label.CENTER));
		
		conTOP.gridx = THREE;
		conTOP.gridy = FIVE;
		conTOP.gridwidth = 1;
		conTOP.gridheight = 1;
		layoutTOP.setConstraints(t_lchance, conTOP);
		panelWORD.add(t_lchance);
		
		// To set Panel relative with Letter Button.
		panelLETTER.setLayout(new GridLayout(THREE, TEN, FIVE, FIVE)); // ���� ������ ����
		for (int i = 0; i < TWENTYSIX; i++){
			panelLETTER.add(b_letter[i]);
		}
		
		// To set Panel relative with Start and Reset Button.
		panelCONTROL.setLayout(layoutBottom);
		conBOTTOM.gridx = 0;
		conBOTTOM.gridy = 0;
		conBOTTOM.gridwidth = TWO;
		conBOTTOM.gridheight = 1;
		layoutBottom.setConstraints(b_start, conBOTTOM);
		panelCONTROL.add(b_start);
		
		conBOTTOM.gridx = TWO;
		conBOTTOM.gridy = 0;
		conBOTTOM.gridwidth = 1;
		conBOTTOM.gridheight = 1;
		layoutBottom.setConstraints(b_reset, conBOTTOM);
		panelCONTROL.add(b_reset);
		
		conBOTTOM.gridx = 0;
		conBOTTOM.gridy = 0;
		conBOTTOM.gridwidth = 0;
		conBOTTOM.gridheight = 1;
		layoutBottom.setConstraints(t_oneshot, conBOTTOM);
		panelCONTROL.add(t_oneshot);
		
		conBOTTOM.gridx = 1;
		conBOTTOM.gridy = 1;
		conBOTTOM.gridwidth = TWO;
		conBOTTOM.gridheight = 1;
		layoutBottom.setConstraints(b_oneshot, conBOTTOM);
		panelCONTROL.add(b_oneshot);
		
		// To set panel for guessword, button letter, button control.
		final JPanel jp = new JPanel(new GridLayout(THREE, 1));
		jp.add(panelWORD);
		jp.add(panelLETTER);
		jp.add(panelCONTROL);
		
		// To apply Total Panel.
		getContentPane().setLayout(new BorderLayout());
		getContentPane().add(panelDRAW, BorderLayout.CENTER);
		getContentPane().add(jp, BorderLayout.WEST);
		// This for loop is to showing up letter button. 
		for (int i = 0; i < TWENTYSIX; i++) { 
			final String str = String.valueOf(c_letter[i]);
			b_letter[i].setActionCommand(str);
			b_letter[i].addActionListener(this);
		}
		
		// To set two buttons ActionLisnter.
		b_start.addActionListener(this);
		b_reset.addActionListener(this);
		b_oneshot.addActionListener(this);
		
		// To set size
		if (System.getProperty("os.name").toLowerCase().indexOf("win") >= 0) {
			super.setSize(WIDTH_ONE, HEIGHT_ONE);
		} else {
			super.setSize(WIDTH_TWO, HEIGHT_TWO);
		}
		// To set location
		super.setLocation(Numbers.TWO_HUNDRED.getNumber(),
				Numbers.TWO_HUNDRED.getNumber());
		super.setResizable(false);
		// To make size not change
		super.setVisible(true);
		panelCONTROL.requestFocus();
	}

	/**
	 *  This is the method to initialize
	 */
	public void aInit() { 
		running = false;
		level = EIGHT;
		failnum = 0;
		t_lchance.setText("");
		t_word.setText("");
		t_tried.setText("");
		t_oneshot.setText("");
		t_msg.setText("Press Start to Play :)");
	}

	/**
	 *  This is to set message depended on appropriate variables. 
	 */
	public void updateText() {
		final StringBuffer selected = new StringBuffer();
		
		boolean blank = false;
		for (int i = 0; i < hm.getAnswer().length(); i++) {
			flag = false;
			
			// This loop is compare between answer string and letter in temp1 array.
			// If they are equal to each other, change the flag value from 'false' to 'true'.
			for (int j = 0; j <= temp1_index; j++){
				if (hm.getAnswer().charAt(i) == temp1[j]){
					flag = true;
				}
			}
			
			// If they are equal to each other, just show up blank space.
			// and insert to the string of selected to determine the letter selected or not. 
			if (flag) { 
				//selected += hm.getAnswer().charAt(i) + "  ";
				selected.append(hm.getAnswer().charAt(i) + "  ");
			} 
			// To show up the number of letters in answer.
			// If there is no same letter between them, just show up '_'
			else {
				//selected += "_   ";
				selected.append("_  ");
				blank = true;
			}
			t_word.setText(selected.toString()); //
		}
		
		// To show up '_' as much as the number of letters in answer string.
		// To show up which letter user selected.
		t_word.setText(selected.toString()); //
		
		// If user won in the hangman, and there is no more blank (blank = false);
		// this program show up a proper message and initialize failnum and stop running.
		if (!blank) {
			t_tried.setText("   YOU WON!!   ");
			t_msg.setText("Press Start to Play :)");
			running = false;
		}
	}

	/**
	 * This is actionPerformed method.
	 * @param e
	 */
	public void actionPerformed(ActionEvent e) {
		
		// To set prebutton background color.
		preButton.setBackground(Color.WHITE);
		
		// To get the letter that already selected from button.
		preButton = (JButton) e.getSource();
		letterButton = (JButton) e.getSource();
		
		// To store the letter in the button label to String label.
		final String label = letterButton.getText();
		
		// To store the changed letter to String lowerCase
		final String lowerCase = label.toLowerCase();
		final char ch = lowerCase.charAt(0);
		
		this.checkAction(ch);
		
		// To set the color of the button to yellow.
		letterButton.setBackground(Color.yellow);
		
		// When user press start button, this game choose one random string from the given array
		// and show up initial chances and appropriate message.
		if (label.endsWith("START")) {
			if (!running) {
				// To initialize
				running = true;
				t_word.setText("");
				t_tried.setText("");
				temp1_index = 0;
				temp2_index = 0;
				
				// To choose randomly word.
				Hangman.reset();
				updateText();
				
				// To show user's own chance.
				t_lchance.setText(String.valueOf(level - failnum));
				t_msg.setText("YOU GOT JUST NEW WORD");
				
				// the textfield of t_oneshot is enable.
				t_oneshot.setEditable(true);
				
				// the button of b_start is not enable.
				b_start.setEnabled(false);
			}
		}
		
		// When user write some string and press 'oneshot' button, this program compare between them each other. 
		// If user's guess is correct, simply game done and show up a proper message.
		// If user's guess is incorrect, simply user lose his(her) own chance as -1 point.
		else if(label.equals("ONESHOT")){
			
			// If this program is not running, simply show up a proper message.
			if(!running){
				t_msg.setText("PLEASE PRESS START");
				t_oneshot.setEditable(false);
			}
			
			// If this program is running.
			if(running){
				t_oneshot.setEditable(true);
				final String s = t_oneshot.getText();
				
				// To compare between user's input and the answer.
				// If they are same, show up a proper message.
				if(hm.getAnswer().equalsIgnoreCase(s)) {
					t_word.setText(hm.getAnswer());
					t_tried.setText("   YOU WON!!   ");
					t_msg.setText("Press Start to Play :)");
					running = false;
				}
				
				// If tehy are not same, simply show up a proper message and lost one chance.
				else if(!hm.getAnswer().equalsIgnoreCase(s)){
					if(!"".endsWith(s)) {
						t_msg.setText("NOP");
						failnum++;
						t_lchance.setText(String.valueOf(level - failnum));
					}
					else t_msg.setText("Please Type something!!!");
				}
			}
			t_oneshot.setText("");
			
		}
		// If user press the button of RESET, calls for the method of AInit();
		else if (label.equals("RESET")) {
			aInit();
			b_start.setEnabled(true);
		}
	}
	
	/**
	 * to check Action
	 * @param ch
	 */
	private void checkAction(char ch) {
		if (ch >= 'a' && ch <= 'z') {
			
			// If user is not playing game, show up appropriate message.
			if (!running){
				t_msg.setText("Press Start to Play :)");
			}
			
			// If user is playing game,
			else {
				flag = false;
				
				// To check the letter already selected or not.
				for (int i = 0; i <= temp2_index; i++) { 
					if (temp2[i] == ch) {
						flag = true;
						break;
					}
				}

				// If user choose the unselected button(flag=false), show up on t_tried(JTextField)
				if (!flag) {
					t_tried.setText(t_tried.getText() + ch + "  "); 
					
					// To store selected letter to temp2 array to compare later.
					temp2_index++;
					temp2[temp2_index] = ch; 
					
					// This loop compare between the selected letter and the string of answer. the variable of 'flag' will be true.
					for (int i = 0; i < hm.getAnswer().length(); i++) {
						if (hm.getAnswer().charAt(i) == ch) {
							flag = true;
						}
					}
					
					// To store selected letter to temp1 array to compare later.
					if (flag) {
						temp1_index++;
						temp1[temp1_index] = ch;
					}
					// If there is no match between string answer and the selected letter, then the variable of failnum increase. 
					else {
						failnum++;
					}
					
					// To show the left chance for user.
					t_lchance.setText(String.valueOf(level - failnum));
					
					// To show the selected letter.
					t_msg.setText("You choose " + "[" + ch + "]"); 

					// If user lost every chances, then this program show up an appropriate message for user.
					if (failnum == level) { 
						t_word.setText("Answer : " + hm.getAnswer());
						t_tried.setText("YOU LOSE! PRESS RESET");
						t_msg.setText("Press Reset to Play :)");
						running = false;
					} 
					
					// When user won in this game, calls for the method of setText();
					else{
						updateText(); 
					}
				}
				
				// This is showing up proper message when user press letter that already selected.
				else {
					t_msg.setText("The letter " + "[" + ch + "]"
							+ " already Selected");
				}
			}
		}
	}

	/**
	 *  This class is for drawing hangman.
	 * @author Acie Park
	 *
	 */
	private class HMpanel extends JPanel {
		
		/**
		 * serialVersionUID for the HMpanel
		 */
		private static final long serialVersionUID = 1L;
		
		/**
		 * This is a contructor for setBackground
		 */
		private HMpanel() {
			super.setBackground(Color.WHITE);
		}

		/**
		 *  To draw hangman
		 *  @param g
		 */
		public void paintComponent(Graphics g) {
			super.paintComponent(g);
			final Graphics2D g2 = (Graphics2D) g;
			final Stroke stroke = new BasicStroke(FIVE,
					BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
			g2.setStroke(stroke);
			
			// To draw basic the device of hanger.
			g2.drawLine(ONETEN, ONESEVENFIVE, ONETHRTY, ONESIXFIVE);
			g2.drawLine(ONEFIFTY, ONESEVENFIVE, ONETHRTY, ONESIXFIVE);
			g2.drawLine(ONETHRTY, THIRTY, ONETHRTY, ONESIXFIVE);
			g2.drawLine(SIXFIVE, THIRTY, ONETHRTY, THIRTY);
			g2.drawLine(SIXFIVE, THIRTY, SIXFIVE, FORTY);

			repaint();

			// If failnum is more than 1, then draw the head of the hangman.
			if (failnum >= 1) {
				g2.setColor(new Color(FIFTY, FIFTY, 0));
				g2.drawOval(FIFTYTHREE, FORTY, TWENTYFIVE, TWENTYFIVE);
			}
			
			// If failnum is more than 2, then draw the body of the hangman.
			if (failnum >= TWO) {
				g2.drawLine(SIXFIVE, SIXFIVE, SIXFIVE, NINETYFIVE);
			}
			
			// If failnum is more than 3, then draw the left_arm of the hangman.
			if (failnum >= THREE) {
				g2.drawLine(SIXFIVE, SIXFIVE, FIFTY, EIGHTYII);
			}
			
			// If failnum is more than 4, then draw the right_arm of the hangman.
			if (failnum >= FOUR) {
				g2.drawLine(SIXFIVE, SIXFIVE, EIGHTYII, EIGHTYII);
			}
			
			// If failnum is more than 5, then draw the left_leg of the hangman.
			if (failnum >= FIVE) {
				g2.drawLine(SIXFIVE, NINETY, FOURFIVE, ONETEN);
			}
			
			// If failnum is more than 6 then draw the right_leg of the hangman.
			if (failnum >= SIX) {
				g2.drawLine(SIXFIVE, NINETY, EIGHTYFIVE, ONETEN);
			}
			
			// If failnum is more than 7 then draw the both foots of the hangman.
			if (failnum >= SEVEN) {
				g2.drawLine(FIVEEIGHT, ONETEN, THIRTYFIVE, ONETEN);
				g2.drawLine(SEVENEIGHT, ONETEN, NINETYFIVE, ONETEN);
			}
			
			// If failnum is 8, then show up an appropriate message.
			if (failnum == EIGHT) {
				g2.setFont(new Font("Monospaced", Font.BOLD + Font.ITALIC, TWENTY));
				g2.drawString("DEAD!! OOPS", FOURFIVE, TWOHUNDREAD);
			}
		}
	}
	
	/**
	 * This is a main method.
	 * @param args
	 */
	public static void main(String args[]) {
		new HangmanGUI();
	}
}